package session

import (
	"github.com/boj/redistore"
	"github.com/gorilla/sessions"
	"net/http"
)

var _defaultStore Proxy

func MustSetup(redisUrl string, keyPairs []byte) {
	_store, err := NewProxy(WithRedisUrl(redisUrl), WithKeyPairs(keyPairs))
	if err != nil {
		panic(err)
	}
	_defaultStore = _store
}

type Proxy interface {
	sessions.Store
	Delete(r *http.Request, w http.ResponseWriter, session *sessions.Session) error
}

type StoreConfig struct {
	RedisUrl string
	KeyPairs []byte
}

type StoreOptions func(*StoreConfig)

func WithRedisUrl(redisUrl string) StoreOptions {
	return func(c *StoreConfig) {
		c.RedisUrl = redisUrl
	}
}

func WithKeyPairs(keyPairs []byte) StoreOptions {
	return func(c *StoreConfig) {
		c.KeyPairs = keyPairs
	}
}

func NewProxy(options ...StoreOptions) (Proxy, error) {
	config := &StoreConfig{
		KeyPairs: []byte("secret"),
	}
	for _, option := range options {
		option(config)
	}
	if config.RedisUrl != "" {
		_store, err := redistore.NewRediStore(10, "tcp", config.RedisUrl, "", config.KeyPairs)
		if err != nil {
			return nil, err
		}
		return proxy{
			Store: _store,
		}, nil
	} else {
		s := sessions.NewCookieStore(config.KeyPairs)
		s.Options.Secure = false
		return proxy{
			Store: s,
		}, nil

	}
}

type proxy struct {
	sessions.Store
}

func (p proxy) Delete(r *http.Request, w http.ResponseWriter, session *sessions.Session) error {
	if rs, ok := p.Store.(*redistore.RediStore); ok {
		return rs.Delete(r, w, session)
	}
	return nil
}

const Key = "session"

func GetSession(r *http.Request) (*sessions.Session, error) {
	return _defaultStore.Get(r, Key)
}

func Get(r *http.Request, key string) (*sessions.Session, error) {
	return _defaultStore.Get(r, key)
}

func NewSession(r *http.Request) (*sessions.Session, error) {
	return _defaultStore.New(r, Key)
}

func New(r *http.Request, key string) (*sessions.Session, error) {
	return _defaultStore.New(r, key)
}

func Save(r *http.Request, w http.ResponseWriter, session *sessions.Session) error {
	return _defaultStore.Save(r, w, session)
}

func Delete(r *http.Request, w http.ResponseWriter, session *sessions.Session) error {
	return _defaultStore.Delete(r, w, session)
}
